#include "main.h"

/**
 * Font
 */
uint32_t *unicodes = NULL, numunicodes = 0;
uint8_t *glyphs = NULL;

/**
 * Resize a pixel buffer with anti-aliasing
 */
void resize(uint32_t *dst, int x, int y, int w, int h, int p, uint32_t *src, int sw, int sh, int sp)
{
    SDL_Surface *srcs, *dsts;
    SDL_Rect srcrect, dstrect;

    if(!dst || !src || w < 1 || h < 1 || p < 1 || sw < 1 || sh < 1 || sp < 1)
        return;
    srcs = SDL_CreateRGBSurfaceFrom(src, sw, sh, 32, sp * 4, 0xFF, 0xFF00, 0xFF0000, 0xFF000000);
    dsts = SDL_CreateRGBSurfaceFrom(dst, p, y + h, 32, p * 4, 0xFF, 0xFF00, 0xFF0000, 0xFF000000);
    srcrect.x = 0; srcrect.y = 0; srcrect.w = sw; srcrect.h = sh;
    dstrect.x = x; dstrect.y = y; dstrect.w = w; dstrect.h = h;
    SDL_SetSurfaceBlendMode(srcs, SDL_BLENDMODE_BLEND);
    SDL_SetSurfaceBlendMode(dsts, SDL_BLENDMODE_BLEND);
    SDL_BlitScaled(srcs, &srcrect, dsts, &dstrect);
    SDL_FreeSurface(srcs); SDL_FreeSurface(dsts);
}

/**
 * Display a text with various parameters into an arbitrary pixel buffer
 */
int text2(int cx, int cy, int cw, int ch, int p, uint32_t *d, int x, int y, uint32_t color, char *str)
{
    int i, j, k, w, ex = cx + cw, ey = cy + ch;
    uint32_t c, *dst;
    uint8_t *glyph;

    if(!str || !p || !d || x >= ex || y >= ey) return x;
    while(*str) {
        if((*str & 128) != 0) {
            if(!(*str & 32)) { c = ((*str & 0x1F)<<6)|(*(str+1) & 0x3F); str++; } else
            if(!(*str & 16)) { c = ((*str & 0xF)<<12)|((*(str+1) & 0x3F)<<6)|(*(str+2) & 0x3F); str += 2; } else
            if(!(*str & 8)) { c = ((*str & 0x7)<<18)|((*(str+1) & 0x3F)<<12)|((*(str+2) & 0x3F)<<6)|(*(str+3) & 0x3F); *str += 3; }
            else c = 0;
        } else c = *str;
        if(!c) break;
        str++;
        if(c < numunicodes) {
            if(c == '\r') { x = cx; continue; }
            if(c == '\n') { x = cx; y += 16; continue; }
            w = unicodes[c] >> 24;
            if(x >= ex || y >= ey) break;
            if(x + w >= cx && y + 16 >= cy) {
                glyph = glyphs + ((unicodes[c] & 0xffffff) << 4);
                dst = d + y * p + x;
                for(j = 0; y + j < ey && j < 16; j++, dst += p, glyph += w >> 3) {
                    for(c = 0x80, i = k = 0; x + i < ex && i < w; i++, c >>= 1) {
                        if(!c) { c = 0x80; k++; }
                        if(x + i >= cx && y + j >= cy && *((uint32_t*)(glyph + k)) & c) dst[i] = color;
                    }
                }
            }
            x += w;
        }
    }
    return x;
}

/**
 * Display a text on screen
 */
int text(int x, int y, uint32_t color, char *str)
{
    return text2(crop_x, crop_y, crop_w, crop_h, screen_p, screen_d, x, y, color, str);
}

/**
 * Return number of pixels required for a string
 */
int textlen(char *str)
{
    int ret = 0;
    uint32_t c;

    if(!str) return 0;
    while(*str) {
        if((*str & 128) != 0) {
            if(!(*str & 32)) { c = ((*str & 0x1F)<<6)|(*(str+1) & 0x3F); str++; } else
            if(!(*str & 16)) { c = ((*str & 0xF)<<12)|((*(str+1) & 0x3F)<<6)|(*(str+2) & 0x3F); str += 2; } else
            if(!(*str & 8)) { c = ((*str & 0x7)<<18)|((*(str+1) & 0x3F)<<12)|((*(str+2) & 0x3F)<<6)|(*(str+3) & 0x3F); *str += 3; }
            else c = 0;
        } else c = *str;
        if(!c) break;
        str++;
        ret += c < numunicodes ? unicodes[c] >> 24 : 0;
    }
    return ret;
}

/**
 * Draw a filled rectangle
 */
void box(int x, int y, int w, int h, uint32_t color)
{
    uint32_t *dst = screen_d + y * screen_p + x;
    int i, j, ex = crop_x + crop_w, ey = crop_y + crop_h;

    if(x + w < crop_x || y + h < crop_y || x >= ex || y >= ey || w < 1 || h < 1) return;
    for(j = 0; j < h && y + j < ey; j++, dst += screen_p)
        if(y + j >= crop_y)
            for(i = 0; i < w && x + i < ex; i++)
                if(x + i >= crop_x) dst[i] = color;
}

/**
 * Draw a rectangle
 */
void rect(int x, int y, int w, int h, uint32_t color1, uint32_t color2)
{
    uint32_t *dst = screen_d + y * screen_p + x;
    int i, j, ex = crop_x + crop_w, ey = crop_y + crop_h;

    if(x + w < crop_x || y + h < crop_y || x >= ex || y >= ey || w < 1 || h < 1) return;
    if(y >= crop_y)
        for(i = 0; i < w && x + i < ex; i++)
           if(x + i >= crop_x) dst[i] = color1;
    for(j = 1, dst += screen_p; j < h - 1 && y + j < ey; j++, dst += screen_p)
        if(y + j >= crop_y) {
            if(x >= crop_x) dst[0] = color1;
            if(x + w - 1 < ex) dst[w - 1] = color2;
        }
    if(y < ey)
        for(i = 0; i < w && x + i < ex; i++)
           if(x + i >= crop_x) dst[i] = color2;
}

/**
 * Draw a text field with a string in it
 */
void txtfld(int x, int y, int w, int h, char *str)
{
    int old = crop_w;

    if(w < 16) w = 16;
    if(h < 16) h = 16;
    box(x, y, w, h, BG - 0x080808);
    if(x + w < crop_x + crop_w) crop_w = x + w - crop_x;
    text(x, y + (h - 17) / 2, FG, str);
    crop_w = old;
}

/**
 * Draw a button
 */
void button(int x, int y, int w, int h, int pressed, char *str)
{
    int len = textlen(str) + 4, unifontfix = 0;
    uint32_t light = BG + 0x202020, dark = BG - 0x202020;

    if(w < len) w = len;
    if(h < 21) h = 21;
    if(w == 21 && str && !strcmp(str, "⏵")) unifontfix = 2;
    if(pressed) {
        rect(x, y, w, h, dark, light);
        box(x + 1, y + 1, w - 2, h - 2, BG);
        y += (h - 21) / 2;
        text(x + unifontfix + (w - len) / 2 + 3, y + 3, BG + 0x080808, str);
        text(x + unifontfix + (w - len) / 2 + 2, y + 2, dark, str);
    } else {
        rect(x, y, w, h, light, dark);
        box(x + 1, y + 1, w - 2, h - 2, BG + 0x080808);
        y += (h - 21) / 2;
        text(x + unifontfix + (w - len) / 2 + 3, y + 2, light, str);
        text(x + unifontfix + (w - len) / 2 + 2, y + 1, BG - 0x303030, str);
    }
}

/**
 * Draw a select box
 */
void selbox(int x, int y, int w, int h, int pressed, char *str)
{
    uint32_t light = BG + 0x202020, dark = BG - 0x202020;
    int old = crop_w;

    if(w < 24) w = 24;
    if(h < 16) h = 16;
    box(x, y, w - 16, h, BG - 0x080808);
    if(x + w - 16 < crop_x + crop_w) crop_w = x + w - 16 - crop_x;
    text(x, y, pressed ? light : FG, str);
    crop_w = old;
    x += w - 16;
    if(pressed) {
        rect(x, y, 16, h, dark, light);
        box(x + 1, y + 1, 14, h - 2, BG);
        y += (h - 16) / 2;
        text(x + 5, y + 2, BG + 0x080808, "▼");
        text(x + 4, y + 1, dark, "▼");
    } else {
        rect(x, y, 16, h, light, dark);
        box(x + 1, y + 1, 14, h - 2, BG + 0x080808);
        y += (h - 16) / 2;
        text(x + 5, y + 1, light, "▼");
        text(x + 4, y, dark, "▼");
    }
}

/**
 * Draw a check box with label
 */
void chkbox(int x, int y, int checked, char *str)
{
    box(x + 1, y + 5, 11, 11, BG - 0x080808);
    if(checked) text(x, y - 2, FG, "✔");
    text(x + 16, y, FG, str);
}

int scrpos, scrbtn, scrcur, scrtot, scrdel;

/**
 * Display horuyontal scroll bar
 */
int hscroll(int x, int y, int h, int cur, int tot, int pressed)
{
    uint32_t light = BG + 0x202020, dark = BG - 0x202020, bg = BG - 0x080808, b = BG + 0x080808;

    scrcur = cur; scrtot = tot;
    if(scrcur > scrtot) scrcur = scrtot;
    if(scrcur < 0) scrcur = 0;

    if(h > scrtot || !scrtot) {
        if(pressed)
            rect(x, y, 10, h, dark, light);
        else
            rect(x, y, 10, h, light, dark);
        box(x + 1, y + 1, 8, h - 2, b);
    } else {
        scrbtn = h * h / tot;
        if(scrbtn < 10) scrbtn = 10;
        scrpos = (h - scrbtn) * scrcur / scrtot;
        box(x, y, 10, scrpos, bg);
        if(pressed) {
            rect(x, y + scrpos, 10, scrbtn, dark, light);
            box(x + 1, y + scrpos + 1, 8, scrbtn - 2, BG);
        } else {
            rect(x, y + scrpos, 10, scrbtn, light, dark);
            box(x + 1, y + scrpos + 1, 8, scrbtn - 2, b);
        }
        box(x, y + scrpos + scrbtn, 10, h - scrpos - scrbtn, bg);
    }
    return 0;
}
